:- ['framework/base.pl'].

containsce(X,Y) :- contains(X,Y).
containsce(X,P) :- hasproperty2(D,P,_), contains(X,D).

haspropertyr(X,X).
haspropertyr(X,Y) :- hasproperty2(X,Y,_).

isto(X1,X2,Y1,Y2) :- hasproperty2(X1,X2,_), hasproperty2(Y1,Y2,_).
isto(X,X,Y,Y).
isto([X1|_],Y1,[X2|_],Y2) :- isto(X1,Y1,X2,Y2).
isto([_|LST1],Y1,[_|LST2],Y2) :- isto(LST1,Y1,LST2,Y2).
isto(pri(T,LST1),X1,pri(T,LST2),X2) :- isto(LST1,X1,LST2,X2).

knowsce1(A,C1,C2) :- C1=ctxm(_,_,_,_,_), der(A,X1),containsce(X1,C1),ce(X1,X2),der(A,X2),X2\=X1,isto(X1,C1,X2,C2),C2\=C1.

%
% knowsceP(+A,+C1,-C2,+L0) --- actor(s) A know that items C1 and C2 are content
%                              equivalent from performing one single content
%                              analysis step, and C2 does not occur in L0
%

knowsceP(A,C1,C2,L0) :- findall(C3,(ce(C1,C3),der(A,X3),containsce(X3,C3),C3\=C1),L1),list_to_set(L1,L2),member(C2,L2),not(member(C2,L0)),knowsce1(A,C1,C2).

%
% knowsce(+A,+X,-Y) --- actor(s) A know that context items X and Y are content
%                        equivalent (by performing any number of content
%                        analysis steps)
% 

knowsceR(_,_,[X|_],_,X).
knowsceR(A,L0,[_|L1],L2,X) :- knowsceR(A,L0,L1,L2,X).
knowsceR(A,L0,[],[X|LST],Y) :-
  findall(Z,knowsceP(A,X,Z,L0),L3),
  list_to_set(L3,L4),
  union(L0,L4,L5),
  union(LST,L4,L6),
  knowsceR(A,L5,L4,L6,Y).

knowsce(A,X,Y) :- knowsceR(A,[X],[X],[X],Y).

%
% knowsceM(+A,+X,+Y) --- actor(s) A know that X,Y are content
%                                equivalent
%

knowsceM(_,[],[]).
knowsceM(A,[X1|LST1],[X2|LST2]) :- knowsceM(A,X1,X2), knowsceM(A,LST1,LST2).
knowsceM(_,X,Y) :- ground(X), ground(Y), X=Y, !.
knowsceM(A,ctxm(A1,B1,C1,D1,E1), ctxm(A2,B2,C2,D2,E2)) :- knowsce(A,ctxm(A1,B1,C1,D1,E1), ctxm(A2,B2,C2,D2,E2)).
knowsceM(A,pri(P,L1),pri(P,L2)) :- knowsceM(A,L1,L2).
	
%
% consca(+A,X) --- Actor(s) A can derive message X. The structure and contents
%                  of message X should be given as input at least
%

consca(A,X) :- ce(X,Y), cons(A,Y), knowsceM(A,Y,X).

derca(A,X) :- ce(X,Y), der(A,Y), knowsceM(A,Y,X).


itdo(AL) :-
  print('% '),print(AL),print('\n'),
  % find all previously detectable items 
  findall(C,(detect(A,C),subset(A,AL),A\=AL),LSTC),
  list_to_set(LSTC,SETC),
  % find all potential new items
  findall(CNT,(der(AL,M),contains(M,C),C=ctxm(_,_,_,_,CNT),der(AL,ctxm(_,_,_,_,CNT)),
             not(member(C,SETC))),LNEWC),
  list_to_set(LNEWC,SNEWC),
  % find all newly detectable items
  findall(C,(member(CNT,SNEWC),C=ctxm(_,_,_,_,CNT),derca(AL,C),
             not(member(C,SETC))),SNEWC2),
  list_to_set(SNEWC2,SNEWC3),
%  findall(C,(der(AL,M),contains(M,C),C=ctxm(_,_,_,_,_),
%             not(member(C,SETC)),derca(AL,C)),LNEWC),
  forall(member(X,SETC),assert(detect(AL,X))),
  forall(member(X,SNEWC3),assert(detect(AL,X))),
  forall(detect(AL,X),(print('detect('),print(AL),print(', '),print(X),print(').\n'))).

itdetect(N,AL) :- not((member(X,AL),length(X,N))), !.
itdetect(N,AL) :- forall((member(X,AL),length(X,N)),itdo(X)), NP1 is N+1, itdetect(NP1,AL).


% --------------
% D. linkability
% --------------

% Linkability, as a property of contexts, comes from observing that the
% same identifier (or entity, which in this implementation is a particular
% case) is used in two contexts. We use a depth-first search strategy very
% similar to that used for content analysis. In particular, we start with
% one context; for every identifier we find other contexts with that identifier
% and try to find evidence for their content equivalence.

%
% lnkble(+A,+C1,-C2,+NO) --- actor(s) A knows that contexts C1, C2 share a common
%                            identifier; and C2 does not occur in list NO
%

lnkble(A,ctx(D1,P1),ctx(D2,P2),NO) :-
  % find all identifiers in that context
  findall(I,(der(A,Y),containsce(Y,ctxm(D1,P1,_,I,I))),L0), list_to_set(L0,L1),
  % for all candidate identifier...
  member(ID,L1),
  % find all possible other contexts
  findall(J,(der(A,Y),containsce(Y,ctxm(D,P,_,ID,ID)),J=ctx(D,P)),L0P), list_to_set(L0P,L1P),
  % for every candidate context...
  member(ctx(D2,P2),L1P), ctx(D2,P2)\=ctx(D1,P1), not(member(ctx(D2,P2),NO)),
  % check linkability
  knowsce1(A,ctxm(D1,P1,_,ID,ID),ctxm(D2,P2,_,ID,ID)).

%
% linkable(+A,+X,Y) --- actor(s) A knows that contexts X, Y are related
%

linkable(A,X,Y) :- linkableR(A,[X],[X],[X],Y).

%
% linkableR(+A,+L0,+L1,+L2,Y) --- Y does not occur in L0, and either it occurs
%                                 in L1, or it is linkable by A to an element
%                                 of L2 (helper function for linkable)
%
linkableR(_,_,[X|_],_,X).
linkableR(A,L0,[_|L1],L2,X) :- linkableR(A,L0,L1,L2,X).
linkableR(A,L0,[],[X|LST],Y) :-
  findall(Z,lnkble(A,X,Z,L0),L3),
  list_to_set(L3,L4),
  union(L0,L4,L5),
  union(LST,L4,L6),
  linkableR(A,L5,L4,L6,Y).






pdis(A,SET) :-
  findall(Y,(know(A,Z),contains(Z,Y),Y=ctxm(_,P,_,_,_),P\=dot),LST),
  list_to_set(LST,SET).
  
pcontexts(A,SET) :-
  findall(ctx(D,P),(der(A,Z),contains(Z,ctxm(D,P,_,_,_)),P\=dot),LST),
  list_to_set(LST,SET).

contextsl([],[]).
contextsl([ctxm(D,P,_,_,_)|LSTP],[ctx(D,P)|RETP]) :- contextsl(LSTP,RETP).
contexts(LST,CTXS) :- contextsl(LST,LSTP), list_to_set(LSTP,CTXS).
  

linkablepart(_,[],[]).
linkablepart(A,[X|LST],[XP|LSTP]) :-
  findall(Z,linkable(A,X,Z),LS),
  intersection(LS,LST,XPR),XP=[X|XPR],
  subtract(LST,LS,REST),
  linkablepart(A,REST,LSTP).

alldots([]).
alldots([ctx(_,dot)|LST]) :- alldots(LST).

partby([],_,[]).
partby([X|LST],PA,DP) :- % all contexts in this profile are dots, so we ignore the profile
  X=ctxm(D,P,_,_,_),
  member(LP,PA),
  member(ctx(D,P),LP),
  alldots(LP),
  findall(Z,(member(Z,LST),Z=ctxm(DZ,PZ,_,_,_),member(ctx(DZ,PZ),LP)),LSTZ),
%  findall(V,member(ctxm(_,_,V,_,_),[X|LSTZ]),LSTV),
%  list_to_set(LSTV,XP),
  subtract(LST,LSTZ,LSTR),
  partby(LSTR,PA,DP), !.
partby([X|LST],PA,[XP|DP]) :-
  X=ctxm(D,P,_,_,_),
  member(LP,PA),
  member(ctx(D,P),LP),
  findall(Z,(member(Z,LST),Z=ctxm(DZ,PZ,_,_,_),member(ctx(DZ,PZ),LP)),LSTZ),
  findall(V,member(ctxm(_,_,V,_,_),[X|LSTZ]),LSTV),
  list_to_set(LSTV,XP),
  subtract(LST,LSTZ,LSTR),
  partby(LSTR,PA,DP).

% nosubsets(L1,L1,R): returns all elements in L1 that are not proper subsets of other elements in L1
nosubsets(_,[],[]).
nosubsets(L1,[X|L],R) :- member(Z,L1), X\=Z, subset(X,Z), nosubsets(L1,L,R), !.
nosubsets(L1,[X|L],[X|R]) :- nosubsets(L1,L,R).

coaknow(A,LR) :- dis(A,L),contexts(L,C),linkablepart(A,C,CP),partby(L,CP,LP),nosubsets(LP,LP,LR).

take(_,0,[]).
take(A,1,[(A,s)]).
take(A,2,[A]).

coalition(L) :-
  member(RU1,[0,1,2]), take(umc1,RU1,L1),
  member(RU2,[0,1,2]), take(umc2,RU2,L2),
  member(RU3,[0,1,2]), take(umc3,RU3,L3),
  member(RCI,[0,1,2]), take(ci,RCI,L4),
  member(RPS,[0,1,2]), take(ps,RPS,L5),
  member(RR,[0,1,2]), take(r,RR,L6),
  append([L1,L2,L3,L4,L5,L6],L),
  L\=[].

coa :-
 findall(L,(coalition(L), coaknow(L,LR),print(L),print('|'),print(LR),print('\n')),_).
